package sideeffect

import (
	"context"
	"errors"
	"github.com/nuttech/bell"
	"github.com/stretchr/testify/mock"
	"github.com/wI2L/jsondiff"
	"gitverse.ru/synapse/kubelatte/pkg/api/common"
	"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/kubeapi"
	"gitverse.ru/synapse/kubelatte/pkg/observability/logs"
	util2 "gitverse.ru/synapse/kubelatte/pkg/operator"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/clientset"
	mocks2 "gitverse.ru/synapse/kubelatte/pkg/operator/controllers/clientset/mocks"
	"gitverse.ru/synapse/kubelatte/pkg/storage"
	"go.uber.org/zap"
	"k8s.io/api/admission/v1beta1"
	v1 "k8s.io/api/core/v1"
	errors2 "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"reflect"
	"sync"
	"testing"
)

func TestNewControllerForServer1(t *testing.T) {
	kbltCli, err := kubeapi.GetKBLTClient()
	if err != nil {
		logs.Errorf("failed get KBLTKubeClient")
		return
	}

	t.Run("success", func(t *testing.T) {
		kubeapi.GetKBLTClient = func() (*clientset.V1Alpha1Client, error) {
			return nil, nil
		}
		kubeapi.GetClient = func() *kubernetes.Clientset {
			return nil
		}
		_ = NewExecutor(kbltCli, &kubernetes.Clientset{})
	})

	t.Run("failed GetKBLTClient", func(t *testing.T) {
		kubeapi.GetKBLTClient = func() (*clientset.V1Alpha1Client, error) {
			return nil, errors.New("err")
		}
		kubeapi.GetClient = func() *kubernetes.Clientset {
			return nil
		}

		c := NewExecutor(kbltCli, &kubernetes.Clientset{})
		if c == nil {
			t.Errorf("Wrong controller, expected %v, got %v", nil, c)
		}
	})
}

func TestController_SendMessageToApplySideEffect1(t *testing.T) {
	type args struct {
		arfields       *common.ARFields
		recreationMode bool
	}
	tests := []struct {
		name             string
		args             args
		trigger          v1alpha1.Trigger
		template         v1alpha1.Template
		sideEffectorMock func() ExecutorI
	}{
		{
			name: "test all",
			sideEffectorMock: func() ExecutorI {
				seMock := NewMockSideEffector(t)
				seMock.On("ApplySideEffect", mock.Anything, mock.Anything).
					Return(nil, errors.New("grpc error")).Maybe()
				return seMock
			},
			args: args{
				arfields: &common.ARFields{
					Operation: v1beta1.Create,
					OldObject: map[string]any{
						"kind":       "Pod",
						"apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace": "kblt-dev",
							"name":      "podik :D ! XD",
							"annotations": map[string]any{
								annotTag: "0",
							},
						}},
					Object: map[string]any{
						"kind":       "Pod",
						"apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace":   "kblt-dev",
							"name":        "podik :D ! XD",
							"annotations": map[string]any{},
						}},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "recreation_mode",
			sideEffectorMock: func() ExecutorI {
				seMock := NewMockSideEffector(t)
				seMock.On("ApplySideEffect", mock.Anything, mock.Anything).
					Return(nil, errors.New("grpc error")).Maybe()
				return seMock
			},

			args: args{
				recreationMode: true,
				arfields: &common.ARFields{
					Operation: v1beta1.Update,
					OldObject: map[string]any{
						"kind":       "Pod",
						"apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace": "kblt-dev",
							"name":      "podik :D ! XD",
							"annotations": map[string]any{
								annotTag: "0",
							},
						}},
					Object: map[string]any{
						"kind":       "Pod",
						"apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace":   "kblt-dev",
							"name":        "podik :D ! XD",
							"annotations": map[string]any{},
						}},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "with generated name",
			sideEffectorMock: func() ExecutorI {
				seMock := NewMockSideEffector(t)
				seMock.On("ApplySideEffect", mock.Anything, mock.Anything).
					Return(nil, errors.New("grpc error")).Maybe()
				return seMock
			},
			args: args{
				arfields: &common.ARFields{
					Object: map[string]interface{}{"kind": "Pod", "apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace":    "kblt-dev",
							"generateName": "podik :D ! XD",
						}},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "test it all without name",
			sideEffectorMock: func() ExecutorI {
				seMock := NewMockSideEffector(t)
				return seMock
			},
			args: args{
				arfields: &common.ARFields{
					Object: map[string]interface{}{"kind": "Pod", "apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace": "kblt-dev",
							"lallla":    "podik :D ! XD",
						}},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "match does not passed",
			sideEffectorMock: func() ExecutorI {
				seMock := NewMockSideEffector(t)
				seMock.On("ApplySideEffect", mock.Anything, mock.Anything).
					Return(nil, errors.New("grpc error")).Maybe()
				return seMock
			},
			args: args{
				arfields: &common.ARFields{
					Object: map[string]interface{}{"kind": "Pod", "apiVersion": "v1",
						"metadata": map[string]interface{}{
							"namespace": "kblt-dev",
							"name":      "podik :D ! XD",
						}},
				},
			},
			trigger: v1alpha1.Trigger{
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Match:        v1alpha1.Match{Kinds: []v1alpha1.Kind{{Kind: []string{"another res"}, ApiGroups: []string{"another api"}}}},
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
	}

	t.Run("disabled kbltSideEffect", func(t *testing.T) {
		logs.Logger = zap.NewNop()
		util2.KbltSideEffect = "disabled"
		seMock := NewMockSideEffector(t)
		c := NewLauncher(seMock)
		c.StartSideEffect(context.Background(), nil, nil)
	})

	util2.KbltSideEffect = "enabled"

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()

			storage.Storage = &storage.StorageController{}
			storage.Storage.Start(false, false)
			storage.Storage.UpdateTemplate(bell.Message{Value: tt.template})
			storage.Storage.UpdateTrigger(bell.Message{Value: tt.trigger})

			c := NewLauncher(tt.sideEffectorMock())
			c.StartSideEffect(context.Background(), tt.args.arfields, nil)
		})
	}
}

func TestController_ApplySideEffectRecreate(t *testing.T) {
	type args struct {
		ctx  context.Context
		info *SE
	}
	tests := []struct {
		name                         string
		args                         args
		trigger                      v1alpha1.Trigger
		template                     v1alpha1.Template
		getTriggerInstanceClientMock func() clientset.TriggerInstanceInterface
		setVars                      func()
	}{
		{
			name: "simple recreation",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return []byte("{\"apiVersion\": \"v1\", \"kind\": \"Pod\",\"metadata\": {\"name\": \"nginx\", \"namespace\": \"namespace_my\", \"uid\": \"uid_my\"},\"spec\": {\"containers\": [{\"name\": \"nginx\",\"image\": \"nginx:1.14.2\", \"ports\": [{\"containerPort\": 80}]}]}}"), nil
				}
			},
			args: args{
				info: &SE{
					SeMode: int32(Recreation),
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
				Spec: v1alpha1.TemplateSpec{Kind: "TriggerInstance", ApiVersion: "kubelatte.synapse.sber/v1alpha1",
					Data: `kind: TriggerInstance
    apiVersion: kubelatte.synapse.sber/v1alpha1
    metadata:
      name: kubelatte-side-effects-test-child
      annotations: 
        kblt/kubelatte-side-effects-test: enabled
    spec:
      data: data`},
			},
			getTriggerInstanceClientMock: func() clientset.TriggerInstanceInterface {
				triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
				triggerInstanceClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Create", mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil).Maybe()

				triggerInstanceClient.On("List", mock.Anything, mock.Anything, mock.Anything).
					Return(&v1alpha1.TriggerInstanceList{Items: []v1alpha1.TriggerInstance{{
						ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{annotSideEffConfigName: "sideEffThis", annotParentHash: "some-other-hash"}},
						Spec:       v1alpha1.TriggerInstanceSpec{},
						Status:     v1alpha1.TriggerInstanceStatus{},
					}}}, nil).Maybe()

				return triggerInstanceClient
			},
		},
		{
			name: "getting parent error",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return nil, errors2.NewNotFound(v1.Resource("pods"), "podik :D! XD")
				}
			},
			args: args{
				info: &SE{
					SeMode: int32(Recreation),
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
				Spec: v1alpha1.TemplateSpec{Kind: "TriggerInstance", ApiVersion: "kubelatte.synapse.sber/v1alpha1",
					Data: `kind: TriggerInstance
    apiVersion: kubelatte.synapse.sber/v1alpha1
    metadata:
      name: kubelatte-side-effects-test-child
      annotations: 
        kblt/kubelatte-side-effects-test: enabled
    spec:
      data: data`},
			},
			getTriggerInstanceClientMock: func() clientset.TriggerInstanceInterface {
				triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
				triggerInstanceClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Create", mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil).Maybe()

				triggerInstanceClient.On("List", mock.Anything, mock.Anything, mock.Anything).
					Return(&v1alpha1.TriggerInstanceList{Items: []v1alpha1.TriggerInstance{{
						ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{annotSideEffConfigName: "sideEffThis", annotParentHash: "some-other-hash"}},
						Spec:       v1alpha1.TriggerInstanceSpec{},
						Status:     v1alpha1.TriggerInstanceStatus{},
					}}}, nil).Maybe()

				return triggerInstanceClient
			},
		},
		{
			name: "parent was not updated",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return []byte("{\"apiVersion\": \"v1\", \"kind\": \"Pod\",\"metadata\": {\"name\": \"nginx\", \"namespace\": \"namespace_my\", \"uid\": \"uid_my\"},\"spec\": {\"containers\": [{\"name\": \"nginx\",\"image\": \"nginx:1.14.2\", \"ports\": [{\"containerPort\": 80}]}]}}"), nil
				}
			},
			args: args{
				info: &SE{
					SeMode: int32(Recreation),
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
			getTriggerInstanceClientMock: func() clientset.TriggerInstanceInterface {
				triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
				triggerInstanceClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Create", mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil).Maybe()

				triggerInstanceClient.On("List", mock.Anything, mock.Anything, mock.Anything).
					Return(&v1alpha1.TriggerInstanceList{Items: []v1alpha1.TriggerInstance{{
						ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{annotSideEffConfigName: "sideEffThis", annotParentHash: "some-other-hash"}},
						Spec:       v1alpha1.TriggerInstanceSpec{},
						Status:     v1alpha1.TriggerInstanceStatus{},
					}}}, nil).Maybe()

				return triggerInstanceClient
			},
		},
		{
			name: "no old TIs",
			getTriggerInstanceClientMock: func() clientset.TriggerInstanceInterface {
				triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
				triggerInstanceClient.On("List", mock.Anything, mock.Anything, mock.Anything).
					Return(&v1alpha1.TriggerInstanceList{Items: []v1alpha1.TriggerInstance{}}, nil)
				return triggerInstanceClient
			},
			setVars: func() {},
			args: args{
				info: &SE{
					SeMode: int32(Recreation),
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
		},
		{
			name: "getting TIs error",
			getTriggerInstanceClientMock: func() clientset.TriggerInstanceInterface {
				triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
				triggerInstanceClient.On("List", mock.Anything, mock.Anything, mock.Anything).
					Return(&v1alpha1.TriggerInstanceList{Items: []v1alpha1.TriggerInstance{}}, errors.New("error tis getting"))
				return triggerInstanceClient
			},
			setVars: func() {},
			args: args{
				info: &SE{
					SeMode: int32(Recreation),
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
		},
		{
			name: "empty parent hash",
			getTriggerInstanceClientMock: func() clientset.TriggerInstanceInterface {
				triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
				triggerInstanceClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("Create", mock.Anything, mock.Anything, mock.Anything).
					Return(nil, nil).Maybe()

				triggerInstanceClient.On("List", mock.Anything, mock.Anything, mock.Anything).
					Return(&v1alpha1.TriggerInstanceList{Items: []v1alpha1.TriggerInstance{{
						ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{annotSideEffConfigName: "sideEffThis"}},
						Spec:       v1alpha1.TriggerInstanceSpec{},
						Status:     v1alpha1.TriggerInstanceStatus{},
					}}}, nil).Maybe()

				return triggerInstanceClient
			},
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return nil, errors2.NewNotFound(schema.GroupResource{}, "Pod test")
				}
			},
			args: args{
				info: &SE{
					SeMode: int32(Recreation),
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
	}

	util2.KbltSideEffect = "enabled"
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.setVars()

			triggerInstanceClient := tt.getTriggerInstanceClientMock()

			kbltKubeClientMock := mocks2.NewV1Alpha1Interface(t)
			kbltKubeClientMock.On("TriggerInstance").Return(triggerInstanceClient).Maybe()
			kbltKubeClientMock.On("TriggerInstance").Return(triggerInstanceClient).Maybe()

			storage.Storage = &storage.StorageController{}
			storage.Storage.Start(false, false)
			storage.Storage.UpdateTemplate(bell.Message{Value: tt.template})
			storage.Storage.UpdateTrigger(bell.Message{Value: tt.trigger})

			c := Executor{kbtlKubeClient: kbltKubeClientMock, mu: &sync.Mutex{}}
			_ = c.ApplySideEffect(context.Background(), tt.args.info)
		})
	}
}

func TestAddTag(t *testing.T) {
	type args struct {
		oldObj  map[string]any
		tag     int64
		patches []jsondiff.Operation
	}
	tests := []struct {
		name string
		args args
		want []jsondiff.Operation
	}{
		{
			name: "no patches no annotations",
			args: args{
				oldObj: map[string]any{
					"kind":       "Pod",
					"apiVersion": "v1",
					"metadata": map[string]interface{}{
						"namespace": "kblt-dev",
						"name":      "podik :D ! XD",
					}},
				tag:     5,
				patches: nil,
			},
			want: []jsondiff.Operation{{
				Value:    map[string]any{"kblt.sideEffect.resource/tag": "5"},
				OldValue: nil,
				Type:     jsondiff.OperationAdd,
				From:     "",
				Path:     "/metadata/annotations",
			}},
		},
		{
			name: "annotation was added in patches",
			args: args{
				oldObj: map[string]any{
					"kind":       "Pod",
					"apiVersion": "v1",
					"metadata": map[string]interface{}{
						"namespace": "kblt-dev",
						"name":      "podik :D ! XD",
						"annotations": map[string]any{
							annotTag: "0",
						},
					}},
				tag: 4,
				patches: []jsondiff.Operation{{
					Value:    map[string]any{"newannot": "5"},
					OldValue: nil,
					Type:     jsondiff.OperationAdd,
					From:     "",
					Path:     "/metadata/annotations",
				},
				}},
			want: []jsondiff.Operation{{
				Value:    map[string]any{"newannot": "5"},
				OldValue: nil,
				Type:     jsondiff.OperationAdd,
				From:     "",
				Path:     "/metadata/annotations",
			}, {
				Value:    "4",
				OldValue: "0",
				Type:     jsondiff.OperationReplace,
				From:     "",
				Path:     "/metadata/annotations/kblt.sideEffect.resource~1tag",
			},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := AddTag(tt.args.oldObj, tt.args.tag, tt.args.patches); !reflect.DeepEqual(got[0], tt.want[0]) {
				t.Errorf("AddTag() = %v, want %v", got, tt.want)
			}
			if len(tt.want) == 1 {
				return
			}
			if got := AddTag(tt.args.oldObj, tt.args.tag, tt.args.patches); !reflect.DeepEqual(got[1].Value, tt.want[1].Value) {
				t.Errorf("AddTag() = %v, want %v", got, tt.want)
			}
			if got := AddTag(tt.args.oldObj, tt.args.tag, tt.args.patches); !reflect.DeepEqual(got[1].Path, tt.want[1].Path) {
				t.Errorf("AddTag() = %v, want %v", got, tt.want)
			}
			if got := AddTag(tt.args.oldObj, tt.args.tag, tt.args.patches); !reflect.DeepEqual(got[1].Type, tt.want[1].Type) {
				t.Errorf("AddTag() = %v, want %v", got, tt.want)
			}
		})
	}
}
